<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试</title>
</head>

<body>

<div style="width: 600px;height: 400px;">
    <p>
        选择图片： <input type="file" id="send-pic">
    </p>

    <p>
        下载大文件: <label for="bigfileMd5"></label><input type="text" id="bigfileMd5"/>
        <button id="download">分块下载</button>
        <button id="download2">整体下载</button>

    </p>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="js/spark-md5/spark-md5.min.js"></script>

<script type="application/javascript">

    function calculateBigFileMD5(file, maxBlobSize) {
        return new Promise((resolve, reject) => {
            let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
            let chunks = Math.ceil(file.size / maxBlobSize)
            let currentChunk = 0
            let spark = new SparkMD5.ArrayBuffer()
            let fileReader = new FileReader();

            fileReader.onload = (e) => {
                console.log(`loading md5 ${currentChunk+1} / ${chunks}`)
                spark.append(e.target.result)
                currentChunk++
                if (currentChunk < chunks) {
                    loadNext()
                } else {
                    console.log('loading md5 completed !')
                    resolve(spark.end())
                }
            }

            // 当发生错误时调用onerror事件
            fileReader.onerror = function () {
                reject('Failed to read file');
            };

            function loadNext() {
                let start = currentChunk * maxBlobSize
                let end = ((start + maxBlobSize) >= file.size) ? file.size : start + maxBlobSize
                fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
            }

            loadNext()
        })
    }

    /**
     * 将file切片，每片大小为 {maxBlobSize}
     * @param file
     * @param maxBlobSize
     * @returns {[]} 文件切片
     */
    function splitFile(file, maxBlobSize) {
        let chunks = [];
        let count = Math.ceil(file.size / maxBlobSize);

        for (let i = 0; i < count; i++) {
            let chunk = file.slice(i * maxBlobSize, (i + 1) * maxBlobSize);
            chunks.push(chunk);
        }
        return chunks;
    }

    function createHttpRequest() {
        return axios.create({
            baseURL: 'http://localhost:9009',
            headers: {
                'Access-Control-Allow-Origin': '*',
                'Access-Control-Allow-Methods': 'GET, POST, OPTIONS'}})
    }

    const maxBlobSize = 1024*1024
    document.querySelector('#send-pic').addEventListener('change', function () {
        let files = this.files;
        if (files && files.length) {
            const file = files[0];
            let index = -1
            const fileName = file.name
            const size = file.size

            const promise = calculateBigFileMD5(file, maxBlobSize)
            promise.then(value => {
                let md5 = value

                //将文件分块
                const blobList = splitFile(file, maxBlobSize)

                let postData = {md5, size, fileName, index, maxBlobSize}

                // 获取分块起始上传索引

                console.log(`请求的数据${JSON.stringify(postData)}`)

                const request = createHttpRequest()
                request({
                    method: 'post',
                    header: {'Content-Type': 'application/json; charset=utf-8'},
                    url: '/upload-bigfile/getStartIndex',
                    data: postData
                }).then(async res => {
                    const data = res.data

                    if (data.code === 0 && !data.data.bigFileExist && data.data.index<blobList.length) {
                        console.log('begin to upload file')
                        // 开始分块上传
                        let startIndex = data.data.index
                        let formData = new FormData()
                        formData.set('md5', postData.md5)
                        formData.set('size', postData.size)
                        formData.set('fileName', postData.fileName)
                        formData.set('maxBlobSize', maxBlobSize)

                        let end_flag = false

                        while (!end_flag) {
                            for (let i = startIndex; i < blobList.length; i++) {
                                let clientIndex = i;
                                formData.set('index', clientIndex)
                                formData.set('blob', blobList[clientIndex])
                                await request({
                                    method: 'post',
                                    header: {'Content-Type:': 'multipart/form-data'},
                                    url: '/upload-bigfile/blob',
                                    data: formData
                                }).then(async res => {
                                    const resData = res.data
                                    console.log(`upload blobs loading ${i}/${blobList.length} ...`)
                                    if (resData.code === 0) {
                                        if (resData.data.status === 1) {
                                            end_flag = true
                                            console.log("upload blobs success!")
                                        }
                                        if (resData.data.status === -1) {
                                            // 文件损毁，上传损毁部分
                                            let defeatIndexes = resData.data.defeatRecords
                                            console.log(`文件损毁，${defeatIndexes}, 开始第一次重传`)
                                            for (const index1 of defeatIndexes) {
                                                formData.set('index', index1)
                                                formData.set('blob', blobList[index1])
                                                await request({
                                                    method: 'post',
                                                    header: {'Content-Type:': 'multipart/form-data'},
                                                    url: '/upload-bigfile/blob',
                                                    data: formData
                                                }).then(res => {
                                                    const resData = res.data
                                                    if (resData.code === 0) {
                                                        end_flag = true
                                                    }
                                                })
                                            }
                                        }
                                    }
                                })

                            }

                        }
                    } else if (data.data.bigFileExist || data.data.index === blobList.length) {
                        console.log('文件已经存在')
                    } else {
                        console.log('服务内部异常', data.msg)
                    }
                }).catch(err => {
                    console.log('获取分块起始上传索引失败', err)
                })

            })

        }
    }, false);


    let btn = document.getElementById('download')
    btn.addEventListener('click', ()=>{
        let fileMD5 = document.getElementById('bigfileMd5').value
        const request = createHttpRequest()
        request({
            method: 'get',
            header: {'Content-Type': 'application/json; charset=utf-8'},
            url: '/upload-bigfile/get-blobLinks',
            params: {md5: fileMD5}
        }).then(async res => {
            const resData = res.data
            if (resData.code === 0 && resData.data.links.length > 0) {
                const blobLinkList = resData.data.links
                const fileName = resData.data.fileName
                let blobCollect = []
                const len = blobLinkList.length
                for (let i = 0; i <len; i++) {
                    const downloadReq = axios.create({
                        timeout: 5000,
                        headers: {
                            'Access-Control-Allow-Origin': '*',
                            'Access-Control-Allow-Methods': 'GET, POST, OPTIONS'
                        }
                    })
                    await downloadReq({
                        method: 'get',
                        responseType: 'blob', //该 responseType: 'blob' 必须添加
                        url: blobLinkList[i]
                    }).then(res => {
                        if (res.status === 200) {
                            blobCollect.push(res.data)
                        }
                    }).catch(err => {
                        console.log(`下载文件异常${err}`)
                    })
                    console.log(`download loading ${i}/${len}...`)
                }
                console.log("download completed!!!")
                // 创建一个临时的URL代表下载链接
                const url = window.URL.createObjectURL(new Blob(blobCollect));
                const link = document.createElement('a');
                link.href = url;
                link.download = fileName
                document.body.appendChild(link);

                // 触发下载
                link.click();
            } else if(resData.data.links.length === 0){
                console.log('文件不存在')
            }
        }).catch(err => {
            console.log(`获取大文件连接异常-${err}`)
        })
    })

    let btn2 = document.getElementById('download2')
    btn2.addEventListener('click', ()=>{
        let fileMD5 = document.getElementById('bigfileMd5').value
        const request = createHttpRequest()
        request({
            method: 'get',
            url: 'download-big-file',
            params: {md5: fileMD5, maxBlobSize: maxBlobSize},
            responseType: 'blob'
        }).then(res => {
            // 从响应头中获取文件名
            const contentDisposition = res.headers['content-disposition'];
            const fileName = contentDisposition.split(";")[1].split("filename=")[1];
            console.log(contentDisposition)
            console.log(fileName)


            // 创建一个临时的URL代表下载链接
            const url = window.URL.createObjectURL(new Blob([res.data]));
            const link = document.createElement('a');
            link.href = url;
            link.download = 'tess.mp4'
            document.body.appendChild(link);

            // 触发下载
            link.click();
        })
    })
</script>